//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>

#include "drvpub.h"
#include "rbuf.h"

#define RBUF_DATA(x)      ((x)->pData)
#define RBUF_SIZE(x)      ((x)->cbSize)
#define RBUF_TYPE(x)      ((x)->cbType)
#define RBUF_WTIDX(x)     ((x)->dwWtIdx)
#define RBUF_RDIDX(x)     ((x)->dwRdIdx)
#define MTRBUF_WAVL(x)    ((x)->cbWrite)
#define MTRBUF_RAVL(x)    (RBUF_SIZE(x) - MTRBUF_WAVL(x))
#define MTRBUF_INDX(x)    ((x)->dwIndex)
#define MTRBUF_LOCK(x, wr)    ((x)->Lock.Lock(wr))
#define MTRBUF_UNLOCK(x)  ((x)->Lock.Unlock())

Boolean RBUF_MT_Init(PMT_RBUF pr, int cbSize, int cbType)
{
    int ToSiz = sizeof(char) * cbSize * cbType;
    ToSiz = GET_UP_ORDER((ToSiz + 4095) >> 12);
    RBUF_DATA(pr) = (UInt8*)DzAllocKernelPages(ToSiz);
    //RBUF_DATA(pr) = (UInt8*)malloc(ToSiz);
    if (NULL == RBUF_DATA(pr)) {
        return FALSE;
    }

    RBUF_SIZE(pr) = cbSize;

    MTRBUF_WAVL(pr) = cbSize;
    RBUF_TYPE(pr) = cbType;
    MTRBUF_INDX(pr) = 0;

    memset( RBUF_DATA(pr), 0, RBUF_SIZE(pr) );

    return TRUE;
}

void RBUF_MT_DeInit(PMT_RBUF pr)
{
    UInt32 ToSiz = GET_UP_ORDER((RBUF_SIZE(pr)* RBUF_TYPE(pr) + 4095) >> 12);
    if (RBUF_SIZE(pr)) {
        DzFreeKernelPages(RBUF_DATA(pr), ToSiz);
        //free(RBUF_DATA(pr));
        RBUF_DATA(pr) = NULL;
    }
    RBUF_SIZE(pr) = 0;
    MTRBUF_WAVL(pr) = 0;
    RBUF_TYPE(pr) = 0;
    MTRBUF_INDX(pr) = 0;
}

void RBUF_MT_Reset(PMT_RBUF pr)
{
    MTRBUF_WAVL(pr) = RBUF_SIZE(pr);
    MTRBUF_INDX(pr) = 0;
    memset( RBUF_DATA(pr), 0, RBUF_SIZE(pr) );
}

//Multiple Thread Safe Functon
UInt32 RBUF_MT_EnQueue(PMT_RBUF pr, UInt8 *pData, UInt32 cbData)
{
    UInt32 lCb, rCb;
    WaitResult wr;

    MTRBUF_LOCK(pr, &wr);
    rCb = cbData;
    lCb = 0;
    if (rCb > MTRBUF_WAVL(pr)) {
        rCb = MTRBUF_WAVL(pr);//Drop Data
    }
    if (0 == rCb) {
        MTRBUF_UNLOCK(pr);
        return 0;
    }
    /****************************************
    |lCb|lCb| U | U | U |rCb|rCb|rCb|rCb|rCb|
      0   1   2   3   4  idx  6   7   8   9
    *****************************************/
    if ((MTRBUF_INDX(pr) + rCb) > RBUF_SIZE(pr)) {
        lCb = MTRBUF_INDX(pr) + rCb - RBUF_SIZE(pr);
        rCb -= lCb;
    }

    memcpy(RBUF_DATA(pr) + RBUF_TYPE(pr) * MTRBUF_INDX(pr), \
        pData, RBUF_TYPE(pr) * rCb);
    MTRBUF_INDX(pr) += rCb;

    if (lCb) {
        pData += (RBUF_TYPE(pr) * rCb);
        memcpy(RBUF_DATA(pr), pData, RBUF_TYPE(pr) * lCb);
        MTRBUF_INDX(pr) = lCb;
        rCb += lCb;
    }
    MTRBUF_WAVL(pr) -= rCb;
    MTRBUF_UNLOCK(pr);

    return rCb;
}

UInt32 RBUF_MT_DeQueue(PMT_RBUF pr, UInt8 *pData, UInt32 cbData)
{
    UInt32 lCb, rCb, idx;
    WaitResult wr;

    MTRBUF_LOCK(pr, &wr);
    rCb = cbData;
    lCb = 0;
    if (rCb > MTRBUF_RAVL(pr)) {
        rCb = MTRBUF_RAVL(pr);//Less Data
    }
    if (0 == rCb) {
        MTRBUF_UNLOCK(pr);
        return 0;
    }
    /****************************************
    | U | N | N | U | U | U | U | U | U | U |
      0  idx  2   3   4   5   6   7   8   9
    *****************************************
    | N | N | N | U | U | N | N | N | N | N |
      0   1   2   3   4  idx  6   7   8   9
    *****************************************/
    idx = ( (MTRBUF_INDX(pr) + MTRBUF_WAVL(pr) ) % RBUF_SIZE(pr));
    if (idx + rCb > RBUF_SIZE(pr)) {
        lCb = idx + rCb - RBUF_SIZE(pr);
        rCb -= lCb;
    }
    memcpy(pData, RBUF_DATA(pr) + RBUF_TYPE(pr) * idx, RBUF_TYPE(pr) * rCb);
    if (lCb) {
        pData += (RBUF_TYPE(pr) * rCb);
        memcpy(pData, RBUF_DATA(pr), RBUF_TYPE(pr) * lCb);
        rCb += lCb;
    }
    MTRBUF_WAVL(pr) += rCb;
    MTRBUF_UNLOCK(pr);

    return rCb;
}

//Multiple Thread UnSafe Function
Boolean RBUF_Init(PRBUF pr, int cbSize, int cbType)
{
    int ToSiz = sizeof(char) * cbSize * cbType;
    ToSiz = GET_UP_ORDER((ToSiz + 4095) >> 12);
    RBUF_DATA(pr) = (UInt8*)DzAllocKernelPages(ToSiz);
    //RBUF_DATA(pr) = (UInt8*)malloc(ToSiz);
    if (NULL == RBUF_DATA(pr)) {
        return FALSE;
    }

    RBUF_SIZE(pr)  = cbSize;
    RBUF_TYPE(pr)  = cbType;
    RBUF_WTIDX(pr) = 1;
    RBUF_RDIDX(pr) = 0;
    memset( RBUF_DATA(pr), 0, RBUF_SIZE(pr) );

    return TRUE;
}

void RBUF_DeInit(PRBUF pr)
{
    UInt32 ToSiz = GET_UP_ORDER((RBUF_SIZE(pr)* RBUF_TYPE(pr) + 4095) >> 12);
    if (RBUF_SIZE(pr)) {
        DzFreeKernelPages(RBUF_DATA(pr), ToSiz);
        //free(RBUF_DATA(pr));
        RBUF_DATA(pr) = NULL;
    }
//    if (RBUF_SIZE(pr)) {
//        free(RBUF_DATA(pr));
//        RBUF_DATA(pr) = NULL;
//    }
    RBUF_SIZE(pr) = 0;
    RBUF_TYPE(pr) = 0;
    RBUF_WTIDX(pr) = 1;
    RBUF_RDIDX(pr) = 0;
}

void RBUF_Reset(PRBUF pr)
{
    RBUF_WTIDX(pr) = 1;
    RBUF_RDIDX(pr) = 0;
    memset( RBUF_DATA(pr), 0, RBUF_SIZE(pr) );
}

Boolean RBUF_IsEmpty(PRBUF pr)
{
    UInt32 wTail, rCb;
    wTail = (RBUF_WTIDX(pr) + RBUF_SIZE(pr) - 1) % RBUF_SIZE(pr);
    rCb = (RBUF_RDIDX(pr) > wTail) ? (RBUF_SIZE(pr) - RBUF_RDIDX(pr) + wTail)\
                                   : (wTail - RBUF_RDIDX(pr));
    if (0 == rCb) {
        return TRUE;
    }
    return FALSE;
}

UInt32 RBUF_EnQueue(PRBUF pr, UInt8 *pData, UInt32 cbData)
{
    /****************************************
    | N | N | U | U | F | N | N | N | N | N |
      0   1  rix  3   4  wix  6   7   8   9
    *****************************************
    | U | N | N | N | N | U | U | U | U | U |
      0  wix  2   3   4  rix  6   7   8   9
    *****************************************/
    UInt32 rIdx, rCb, lCb;
    UInt8 *pbData = pData;
    rIdx = RBUF_RDIDX(pr);
    rCb = (RBUF_WTIDX(pr) > rIdx) ? (RBUF_SIZE(pr) - RBUF_WTIDX(pr) + rIdx)\
                                  : (rIdx - RBUF_WTIDX(pr));
    //To Do EnQueue
    lCb = 0;
    if (rCb > cbData) {
        rCb = cbData;
    }

    if (0 == rCb) {
        return 0;
    }

    if ((RBUF_WTIDX(pr) + rCb) > RBUF_SIZE(pr)) {
        lCb = RBUF_WTIDX(pr) + rCb - RBUF_SIZE(pr);
        rCb -= lCb;
    }

    memcpy(RBUF_DATA(pr) + RBUF_TYPE(pr) * RBUF_WTIDX(pr), \
        pbData, RBUF_TYPE(pr) * rCb);

    if (lCb) {
        pbData += (RBUF_TYPE(pr) * rCb);
        memcpy(RBUF_DATA(pr), pbData, RBUF_TYPE(pr) * lCb);
        rCb += lCb;
    }
    //End
    RBUF_WTIDX(pr) = (RBUF_WTIDX(pr) + rCb) % RBUF_SIZE(pr);

    return rCb;
}

UInt32 RBUF_DeQueue(PRBUF pr, UInt8 *pData, UInt32 cbData)
{

    UInt32 wTail, rStart, rCb, lCb;
    UInt8 *pbData = pData;
    wTail = (RBUF_WTIDX(pr) + RBUF_SIZE(pr) - 1) % RBUF_SIZE(pr);
    rCb = (RBUF_RDIDX(pr) > wTail) ? (RBUF_SIZE(pr) - RBUF_RDIDX(pr) + wTail)\
                                   : (wTail - RBUF_RDIDX(pr));

    //To Do DeQueue
    lCb = 0;
    if (rCb > cbData) {
        rCb = cbData;
    }

    if (0 == rCb) {
        return 0;
    }
    rStart = (RBUF_RDIDX(pr) + 1) % RBUF_SIZE(pr);

    if ((rStart + rCb) > RBUF_SIZE(pr)) {
        lCb = rStart + rCb - RBUF_SIZE(pr);
        rCb -= lCb;
    }

    memcpy(pbData, RBUF_DATA(pr) + RBUF_TYPE(pr) * rStart, \
                RBUF_TYPE(pr) * rCb);

    if (lCb) {
        pbData += (RBUF_TYPE(pr) * rCb);
        memcpy(pbData, RBUF_DATA(pr), RBUF_TYPE(pr) * lCb);
        rCb += lCb;
    }
    //End
    RBUF_RDIDX(pr) = (RBUF_RDIDX(pr) + rCb) % RBUF_SIZE(pr);

    return rCb;
}
